// single_threaded_execution.cpp


#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <ctime>

#include <chrono>
#include <mutex>
#include <string>
#include <thread>


namespace blogs
{


using std::chrono::milliseconds;
using std::lock_guard;
using std::mutex;
using std::string;


class Gate
{

  private:

    int
    __counter__;


    string
    __name__;


    string
    __address__;


    mutable
    mutex
    __mtx__;


    void
    __check__()
    {
        if (__name__[0] != __address__[0])
            std::printf("%s\n", ("***** BROKEN *****" + to_string()).c_str());
        else
            std::printf("%s\n", ("******* OK *******" + to_string()).c_str());
    }


  public:

    Gate()
        : __counter__(0), __name__("Nobody"), __address__("Nowhere")
    {
    }


    void
    pass(string name, string address)
    {
        lock_guard<mutex> guard(__mtx__);
        ++__counter__;
        __name__ = name;
        __address__ = address;
        __check__();
    }


    string
    to_string() const
    {
        string s;
        s += " No." + std::to_string(__counter__);
        s += ": " + __name__;
        s += ", " + __address__;
        return s;
    }

};


class User
{

  private:

    string
    __name__;


    string
    __address__;


    Gate &
    __gate__;


  public:

    User(string name, string address, Gate & gate)
        : __name__(name), __address__(address), __gate__(gate)
    {
    }


    void
    run()
    {
        std::printf("%s\n", (__name__ + " BEGIN").c_str());
        while (true) {
            __gate__.pass(__name__, __address__);
            std::this_thread::sleep_for(milliseconds(std::rand() % 1000));
        }
    }

};


void
bye(int sig = SIGTERM)
{
    std::printf("\nBye Single Threaded Execution...\n\n");
    std::exit(sig);
}


} // end of namespace blogs


int
main(int argc, char * argv[])
{
    using std::thread;
    using blogs::Gate;
    using blogs::User;

    std::signal(SIGINT, blogs::bye);
    std::srand(std::time(0));

    Gate gate;
    User alice("Alice", "Alaska", gate);
    User bobby("Bobby", "Brazil", gate);
    User chris("Chris", "Canada", gate);

    thread t1(&User::run, &alice);
    thread t2(&User::run, &bobby);
    thread t3(&User::run, &chris);

    t1.join();
    t2.join();
    t3.join();

    blogs::bye();
    return 0;
}

